/*
 * Copyright (C) 2013 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.picasso;

import ohos.app.Context;
import ohos.media.image.PixelMap;

import static com.squareup.picasso.Utils.KEY_SEPARATOR;

/**
 * A memory cache which uses a least-recently used eviction policy.
 */
public final class LruCache implements Cache {
    /**
     * The Cache.
     */
    ohos.utils.LruBuffer<String, BitmapAndSize> cache;

    /**
     * Create a cache using an appropriate portion of the available RAM as the maximum size.
     *
     * @param context the context
     */
    public LruCache(Context context) {
        this(Utils.calculateMemoryCacheSize(context));
    }

    /**
     * Create a cache with a given maximum size in bytes.
     *
     * @param maxByteCount the max byte count
     */
    public LruCache(int maxByteCount) {
        cache = new ohos.utils.LruBuffer<String, BitmapAndSize>(maxByteCount) {
            protected int sizeOf(String key, BitmapAndSize value) {
                return value.byteCount;
            }
        };
    }

    @Override public PixelMap get(String key) {
        BitmapAndSize bitmapAndSize = cache.get(key);
        return bitmapAndSize != null ? bitmapAndSize.bitmap : null;
    }

    @Override
    public void set(String key, PixelMap bitmap) {
        if (key == null || bitmap == null) {
            throw new NullPointerException("key == null || bitmap == null");
        }

        int byteCount = Utils.getBitmapBytes(bitmap);

        // If the bitmap is too big for the cache, don't even attempt to store it. Doing so will cause
        // the cache to be cleared. Instead just evict an existing element with the same key if it
        // exists.
        if (byteCount > maxSize()) {
            cache.remove(key);
            return;
        }

        cache.put(key, new BitmapAndSize(bitmap, byteCount));
    }

    @Override
    public int size() {
        return cache.size();
    }

    @Override
    public int maxSize() {
        return cache.capacity();
    }

    @Override
    public void clear() {
        cache.clear();
    }

    @Override
    public void clearKeyUri(String uri) {
        // Keys are prefixed with a URI followed by '\n'.
        for (String key : cache.keys()) {
            if (key.startsWith(uri)
                    && key.length() > uri.length()
                    && key.charAt(uri.length()) == KEY_SEPARATOR) {
                cache.remove(key);
            }
        }
    }

    /**
     * Hit count int.
     *
     * @return the Returns the number of times {@link #get} returned a value.
     */
    public int hitCount() {
        return cache.getMatchCount();
    }

    /**
     * Returns the number of times {@link #get} returned {@code null}.
     *
     * @return the int value
     */
    public int missCount() {
        return cache.getMissCount();
    }

    /**
     * Returns the number of times {@link #set(String, PixelMap)} was called.
     *
     * @return the int value
     */
    public int putCount() {
        return cache.getPutCount();
    }

    /**
     * Returns the number of values that have been evicted.
     *
     * @return the int value
     */
    public int evictionCount() {
        return cache.getPutCount();
    }

    static final class BitmapAndSize {
        final PixelMap bitmap;
        final int byteCount;

        /**
         * Instantiates a new Bitmap and size.
         *
         * @param bitmap    the bitmap
         * @param byteCount the byte count value
         */
        BitmapAndSize(PixelMap bitmap, int byteCount) {
            this.bitmap = bitmap;
            this.byteCount = byteCount;
        }
    }
}
